package com.acme;

import com.acme.hash.SmearIntHashFunctionImpl;
import com.acme.hashmap.DirectAddressedHashMapIntToInt;
import com.acme.hashmap.LinkedHashMapIntToInt;
import com.acme.hashmap.MapIntToInt;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Random;

public class PerfMain {

    private static Random  rand = new Random();

    public static void main(String[] args) throws IOException {



        int[] M = {100,1000,2000,3000,10000,100000,150000,200000,300000,400000,500000,600000,1000000};
        PrintWriter printWriter = new PrintWriter(new FileWriter("out.txt"));
        String outputinit = String.format("%s\t%s\t%s\t%s\t%s\t%s", "numberOfElements", "array" , "linked", "java hashmap", "our linked hashmap", "our direct hashmap");
        printWriter.println(outputinit);
        System.out.println(outputinit);

        for(int numOfElements:M){
            // fixtures
            int numOfInsertions = Math.min(1000, Math.round(numOfElements / 10));
            ArrayList<Object> arrayList = new ArrayList<Object>(numOfElements+2*numOfInsertions);
            LinkedList<Object> linkedList = new LinkedList<Object>();
            HashMap<Integer,Integer> hashMap = new HashMap<Integer, Integer>();
            MapIntToInt hashMapIntToInt = new LinkedHashMapIntToInt(new SmearIntHashFunctionImpl());
            MapIntToInt directHashMapIntToInt = new DirectAddressedHashMapIntToInt(new SmearIntHashFunctionImpl());
            int[] randInd = new int[numOfInsertions];

            for (int i = 0; i < numOfInsertions; i++) {
                randInd[i]= randInt(1, numOfElements - 2);
            }

            for (int i = 0;i<numOfElements; i++){
                arrayList.add(new Object());
                linkedList.add(new Object());
                hashMap.put(i, i);
                hashMapIntToInt.put(i, i);
            }
            //ok, let's test!

            //1: Array list
            long arrayListStart = System.nanoTime();
            for (int i = 0; i < numOfInsertions; i++) {
                arrayList.add(randInd[i],new Object());
            }
            long arrayListTime = System.nanoTime() - arrayListStart;
            // Done!
            //1: Linked list
            long linkedListStart = System.nanoTime();
            for (int i = 0; i < numOfInsertions; i++) {
                linkedList.add(randInd[i],new Object());
            }
            long linkedListTime = System.nanoTime() - linkedListStart;
            // Done!

            //3: Hashmap
            long hashmapStart = System.nanoTime();
            for (int i = 0; i < numOfInsertions; i++) {
                hashMap.put(randInd[i],2*randInd[i]);
            }
            long hashmapTime = System.nanoTime() - hashmapStart;
            // Done!
            //4: OurHashmap
            long ourHashmapStart = System.nanoTime();
            for (int i = 0; i < numOfInsertions; i++) {
                hashMapIntToInt.put(randInd[i], 2 * randInd[i]);
            }
            long ourHashmapTime = System.nanoTime() - ourHashmapStart;

            //4: OurDirectHashmap
            long ourDirectHashmapStart = System.nanoTime();
            for (int i = 0; i < numOfInsertions; i++) {
                directHashMapIntToInt.put(randInd[i], 2 * randInd[i]);
            }
            long ourDirectHashmapTime = System.nanoTime() - ourDirectHashmapStart;
            // Done!
            String output = String.format("%s\t%s\t%s\t%s\t%s\t%s",
                    numOfElements,
                    arrayListTime/numOfInsertions,
                    linkedListTime/numOfInsertions,
                    hashmapTime/numOfInsertions,
                    ourHashmapTime/numOfInsertions,
                    ourDirectHashmapTime/numOfInsertions
                );
            printWriter.println(output);
            System.out.println(output);
        }
        printWriter.flush();
        printWriter.close();
        System.out.println("Done!");


    }

    public static int randInt(int min, int max) {
        // nextInt is normally exclusive of the top value,
        // so add 1 to make it inclusive

        return rand.nextInt((max - min) + 1) + min;
    }


}
